/*
 * udp-p2sp.cc
 *
 *  Created on: 2009-11-2
 *      Author: root
 */

#include "udp-p2sp.h"
#include "rtp.h"
#include "random.h"
#include <stdio.h>
#include <string.h>


int hdr_p2sp::offset_;


//p2sp Application Header Class
static class p2spHeaderClass : public PacketHeaderClass {
  public:
          p2spHeaderClass() : PacketHeaderClass("PacketHeader/p2sp",
                                                  sizeof(hdr_p2sp)) {
            bind_offset(&hdr_p2sp::offset_);
          }
}class_hdr_p2sp;


// Udp2spAgent OTcl linkage class
static class Udp2spAgentClass : public TclClass {
public:
        Udp2spAgentClass() : TclClass("Agent/UDP/UDP2SP") {}
        TclObject* create(int, const char*const*) {
                return (new Udp2spAgent());
        }
} class_udp2sp_agent;


// Constructor (with no arg)
Udp2spAgent::Udp2spAgent() : UdpAgent()
{
        support_p2sp_ = 0;
        seq_ = 0;
}

Udp2spAgent::Udp2spAgent(packet_t type) : UdpAgent(type)
{
        support_p2sp_ = 0;
        seq_ = 0;
}


// Add Support of Multimedia Application to UdpAgent::sendmsg
void Udp2spAgent::sendmsg(int nbytes, const char* flags)
{
}


void Udp2spAgent::sendmsg(int nbytes, AppData* data, const char *flags) {

}
void Udp2spAgent::sendto(int nbytes, AppData* data, const char *flags, nsaddr_t dst){
      Packet *p;
      int n,remain,sz, data_offset, seqno,i;
      assert (size_ > 0);

                n = (nbytes / size_) + (nbytes % size_  ? 1:0);
                remain = nbytes % size_;

                if (nbytes == -1) {
                        printf("Error: sendmsg() for UDP should not be -1\n");
                        return;
                }
                ++seq_;
                data_offset = 0;
                seqno = 0;

                double local_time = Scheduler::instance().clock();
                while (n-- > 0) {
                        p = allocpkt();
                        if(n==0 && remain>0) sz = remain;
                        else sz = size_;
                        hdr_cmn::access(p)->size() = sz;

                        hdr_rtp* rh = hdr_rtp::access(p);
                        rh->flags() = 0;
                        rh->seqno() = ++seqno_;
                        hdr_ip* ih = hdr_ip::access(p);
                        ih->saddr() = addr();
                        ih->daddr() = dst;
                        hdr_cmn::access(p)->timestamp() =
                            (u_int32_t)(SAMPLERATE*local_time);
                        // add "beginning of talkspurt" labels (tcl/ex/test-rcvr.tcl)

                        hdr_p2sp* hp = hdr_p2sp::access(p);

                        hp->seq = seq_;
                        hp->nbytes = nbytes;
                        hp->src_addr = addr();
                        hp->dst_addr = dst;
                        hp->data_off_ = data_offset;

                        if (flags && (0 ==strcmp(flags, "NEW_BURST")))
                                rh->flags() |= RTP_M;
                        PacketData* pdata = new PacketData(sz);
                        memcpy(pdata->data(), ((PacketData*)data)->data() + data_offset, sz);
                        data_offset += sz;
/*                      printf("send buf:");
                       for(i =0; i < pdata->size(); i++)
                           {
                           printf("%02X ", *(pdata->data()+i));
                           }
                       printf("\n");*/
                        p->setdata(pdata);
                        /*fprintf(stderr, "udp send data size :%d \n", sz);*/
                        target_->recv(p);

                }
                if(data)
                    delete data;
                idle();
}

// Support Packet Re-Assembly and Multimedia Application
void Udp2spAgent::recv(Packet* p, Handler*)
{
        hdr_ip* ih = hdr_ip::access(p);
        int bytes_to_deliver = hdr_cmn::access(p)->size() - 20;
        int i;


        if (app_ ) {
                    // If an application is attached, pass the data to the app
                    hdr_cmn* cmh = hdr_cmn::access(p);
                    hdr_p2sp* hp = hdr_p2sp::access(p);


                    asm_t::iterator it = asm_info.find(hp->src_addr);
                    if (it == asm_info.end())
                       {
							/*asm_p2sp_t ass_data;
							ass_data.seq = hp->seq;
							ass_data.tbytes = hp->nbytes;
							ass_data.rbytes = bytes_to_deliver;
							memcpy(ass_data.buf + hp->data_off_, ((PacketData *)p->userdata())->data(), bytes_to_deliver);
							asm_info.insert(hp->src_addr, ass_data);*/
							asm_info[hp->src_addr].seq = hp->seq;
                            asm_info[hp->src_addr].tbytes = hp->nbytes;
                            asm_info[hp->src_addr].rbytes = bytes_to_deliver;
                            if(hp->data_off_ + bytes_to_deliver > BUFSIZE) assert(false);
                            memcpy(asm_info[hp->src_addr].buf + hp->data_off_, ((PacketData *)p->userdata())->data(), bytes_to_deliver);
                    }else if (asm_info[hp->src_addr].seq == hp->seq){
                            asm_info[hp->src_addr].rbytes += bytes_to_deliver;
                            if(hp->data_off_ + bytes_to_deliver > BUFSIZE) assert(false);
                            memcpy(asm_info[hp->src_addr].buf + hp->data_off_, ((PacketData *)p->userdata())->data(), bytes_to_deliver);
                       }
                    else {
                            asm_info[hp->src_addr].seq = hp->seq;
                            asm_info[hp->src_addr].tbytes = hp->nbytes;
                            asm_info[hp->src_addr].rbytes = bytes_to_deliver;
                            if(hp->data_off_ + bytes_to_deliver > BUFSIZE) assert(false);
                            memcpy(asm_info[hp->src_addr].buf + hp->data_off_, ((PacketData *)p->userdata())->data(), bytes_to_deliver);
                       }

                    // if fully reassembled, pass the packet to application

                    if(asm_info[hp->src_addr].tbytes == asm_info[hp->src_addr].rbytes) {
                           PacketData* rx_data = new PacketData(asm_info[hp->src_addr].tbytes);
                           memcpy(rx_data->data(), asm_info[hp->src_addr].buf, asm_info[hp->src_addr].tbytes);
                           app_->process_data(asm_info[hp->src_addr].tbytes, rx_data);
                           delete rx_data;
                    }
                    else{
						   PacketData* rx_state = new PacketData(sizeof(p2sp_data_t));
                           p2sp_data_t* pdata = (p2sp_data_t*)(rx_state->data());
                           pdata->type_ = IS_RECVING;
                           pdata->src_id_ = hp->src_addr;
                           pdata->nbytes_ =  asm_info[hp->src_addr].rbytes;
                           app_->process_data(sizeof(p2sp_data_t), rx_state);
                           delete rx_state;
                    }
          }
         Packet::free(p);

}
